home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-02 / ovrsize.zip / OVRSIZ.PAS < prev   
Pascal/Delphi Source File  |  1993-01-04  |  17KB  |  587 lines

  1. {
  2. OVRSIZ generates a report describing an overlaid Turbo Pascal 5.0 program.
  3.  
  4. To use it, first compile the overlaid application to create both an EXE file
  5. and a corresponding MAP file. OVRSIZ must read the first section of the MAP
  6. file (the segment map) to get certain information. It also reads from the EXE
  7. file to get detailed information about the overlays. It doesn't need to access
  8. the OVR file.
  9.  
  10. Call OVRSIZ as follows:
  11.  
  12.   OVRSIZ [Options] ProgName [>Output]
  13.  
  14. OVRSIZ forces the extension 'EXE' onto ProgName to find the executable file,
  15. and the extension 'MAP' onto ProgName to find the MAP file. The overlay report
  16. is written to the standard output and may be redirected to a file or to the
  17. printer. The only option at present is /Q, which stops OVRSIZ from writing
  18. status messages while it works.
  19.  
  20. The OVRSIZ reports is largely self-explanatory. See OVRSIZ.DOC for more
  21. information.
  22.  
  23. Written by Kim Kokkonen, TurboPower Software
  24. Copyright (c) 1989, TurboPower Software. All rights reserved.
  25. May be distributed freely, but not for a profit.
  26.  
  27. Version 1.0, 2/12/89
  28. --------------------
  29.   Initial release.
  30. }
  31.  
  32. {$R-,S-,I-,V-,F-,B-}
  33.  
  34. program OvrSize;
  35.   {-Determine unit sizes in overlaid TP5 EXE files}
  36.  
  37. uses
  38.   Dos;
  39.  
  40. const
  41.   Version = '1.0';                {Version number}
  42.   MaxUnits = 255;                 {Maximum units in a program}
  43.   OvrInstr : Word = $3FCD;        {INT 3Fh instruction}
  44.   ShowStatus : Boolean = True;    {True to keep status running during operation}
  45.   BufSize = 1024;                 {Size of text I/O buffer}
  46.   NameSize = 15;                  {Maximum reported segment name length}
  47.  
  48.   Digits : array[0..$F] of Char = '0123456789ABCDEF';
  49.   DosDelimSet : set of Char = ['\', ':', #0];
  50.  
  51. type
  52.   Pathname = String[79];
  53.  
  54.   Long =
  55.     record
  56.       LowWord, HighWord : Word;
  57.     end;
  58.  
  59.   ExeHeaderRec =                  {Information describing EXE file}
  60.     record
  61.       Signature : Word;           {EXE file signature}
  62.       LengthRem : Word;           {Number of bytes in last page of EXE image}
  63.       LengthPages : Word;         {Number of 512 byte pages in EXE image}
  64.       NumReloc : Word;            {Number of relocation items}
  65.       HeaderSize : Word;          {Number of paragraphs in EXE header}
  66.       MinHeap : Word;             {Minimum extra paragraphs to allow}
  67.       MaxHeap : Word;             {Paragraphs to keep beyond end of image}
  68.       StackSeg : Word;            {Initial stack seg relative to image base}
  69.       StackPtr : Word;            {Initial SP}
  70.       CheckSum : Word;            {EXE file check sum, not used}
  71.       IpInit : Word;              {Initial IP}
  72.       CodeSeg : Word;             {Initial code seg relative to image base}
  73.       RelocOfs : Word;            {Bytes into EXE for first relocation item}
  74.       OverlayNum : Word;          {Overlay number, not used here}
  75.     end;
  76.  
  77.   OverlayRecord =                 {At start of each overlay stub segment}
  78.     record
  79.       Sig : Word;
  80.       Init : Word;
  81.       FileOfs : LongInt;
  82.       CodeSize : Word;
  83.       FixupSize : Word;
  84.       EntryPts : Word;
  85.     end;
  86.  
  87.   UnitRecord =                    {Description of each unit in program}
  88.     record
  89.       SegClass : Word;            {0 for code, 1 for data, 2 for stack, 3 for heap}
  90.       StatStart : LongInt;        {Start position within EXE image in bytes}
  91.       StatLen : LongInt;          {Length of unit image in bytes}
  92.       Name : String[NameSize];    {Name of the unit}
  93.       Ovr : OverlayRecord;
  94.     end;
  95.   UnitArray = array[1..MaxUnits] of UnitRecord;
  96.  
  97.  
  98. var
  99.   Units : UnitArray;              {Describes all segments}
  100.   UnitCount : Word;               {Number of units}
  101.   Iname : Pathname;               {Input EXE file name}
  102.   Mname : Pathname;               {Input MAP file name}
  103.   EXEHeader : ExeHeaderRec;       {Header for input EXE file}
  104.   InF : file;                     {Input EXE file}
  105.   StdErr : Text;                  {Standard error device}
  106.   Buffer : array[1..BufSize] of Char; {Text buffer for map file}
  107.  
  108.   procedure WriteCopyRight;
  109.     {-Copyright notice in object code and on screen}
  110.   begin                           {WriteCopyRight}
  111.     WriteLn(StdErr, 'TP5 Overlay Size Analyzer, by TurboPower Software. Version ', Version);
  112.   end;                            {WriteCopyRight}
  113.  
  114.   procedure OpenStdErr;
  115.     {-Open standard error device}
  116.   begin
  117.     Assign(StdErr, '');
  118.     Rewrite(StdErr);
  119.     with TextRec(StdErr) do begin
  120.       Handle := 2;
  121.       BufSize := 1;
  122.     end;
  123.   end;
  124.  
  125.   procedure Error(Msg : String);
  126.     {-Report error and halt}
  127.   begin
  128.     if Msg <> '' then
  129.       WriteLn(Msg);
  130.     Halt(1);
  131.   end;
  132.  
  133.   procedure InvalidMapError;
  134.     {-Common error}
  135.   begin
  136.     Error('Invalid MAP file format');
  137.   end;
  138.  
  139.   procedure ErrorExeRead;
  140.     {-Common error}
  141.   begin
  142.     Error('Error reading EXE file');
  143.   end;
  144.  
  145.   function HexW(W : Word) : String;
  146.     {-Return hex string for word}
  147.   begin
  148.     HexW[0] := #4;
  149.     HexW[1] := Digits[Hi(W) shr 4];
  150.     HexW[2] := Digits[Hi(W) and $F];
  151.     HexW[3] := Digits[Lo(W) shr 4];
  152.     HexW[4] := Digits[Lo(W) and $F];
  153.   end;
  154.  
  155.   function HexL(L : LongInt) : String;
  156.     {-Return hex string for LongInt}
  157.   begin
  158.     with Long(L) do
  159.       HexL := HexW(HighWord)+HexW(LowWord);
  160.   end;
  161.  
  162.   function Long2Str(L : LongInt) : String;
  163.     {-Convert a long/word/integer/byte/shortint to a string}
  164.   var
  165.     S : String;
  166.   begin
  167.     Str(L, S);
  168.     Long2Str := S;
  169.   end;
  170.  
  171.   function StUpcase(S : String) : String;
  172.     {-Return the uppercase of a string}
  173.   var
  174.     I : Integer;
  175.   begin
  176.     for I := 1 to Length(S) do
  177.       S[I] := Upcase(S[I]);
  178.     StUpcase := S;
  179.   end;
  180.  
  181.   function Pad(S : String; Len : Byte) : String;
  182.     {-Return a string right-padded to length len with ch}
  183.   var
  184.     O : String;
  185.   begin
  186.     if Length(S) >= Len then
  187.       Pad := S
  188.     else begin
  189.       O[0] := Chr(Len);
  190.       Move(S[1], O[1], Length(S));
  191.       FillChar(O[Succ(Length(S))], Len-Length(S), ' ');
  192.       Pad := O;
  193.     end;
  194.   end;
  195.  
  196.   function TrimLead(S : String) : String;
  197.     {-Return a string with leading white space removed}
  198.   begin
  199.     while (Length(S) > 0) and (S[1] <= ' ') do
  200.       Delete(S, 1, 1);
  201.     TrimLead := S;
  202.   end;
  203.  
  204.   function Trim(S : String) : String;
  205.     {-Return a string with leading and trailing white space removed}
  206.   begin
  207.     while (Length(S) > 0) and (S[Length(S)] <= ' ') do
  208.       Dec(S[0]);
  209.     while (Length(S) > 0) and (S[1] <= ' ') do
  210.       Delete(S, 1, 1);
  211.     Trim := S;
  212.   end;
  213.  
  214.   function HasExtension(Name : String; var DotPos : Word) : Boolean;
  215.     {-Return whether and position of extension separator dot in a pathname}
  216.   var
  217.     I : Word;
  218.   begin
  219.     DotPos := 0;
  220.     for I := Length(Name) downto 1 do
  221.       if (Name[I] = '.') and (DotPos = 0) then
  222.         DotPos := I;
  223.     HasExtension := (DotPos > 0) and (Pos('\', Copy(Name, Succ(DotPos), 64)) = 0);
  224.   end;
  225.  
  226.   function ForceExtension(Name, Ext : String) : String;
  227.     {-Return a pathname with the specified extension attached}
  228.   var
  229.     DotPos : Word;
  230.   begin
  231.     if HasExtension(Name, DotPos) then
  232.       ForceExtension := Copy(Name, 1, DotPos)+Ext
  233.     else
  234.       ForceExtension := Name+'.'+Ext;
  235.   end;
  236.  
  237.   function BlkRead(var F : file; var Buffer; Size : Word) : Boolean;
  238.     {-Convenient shell around BlockRead}
  239.   var
  240.     BytesRead : Word;
  241.   begin
  242.     BlockRead(F, Buffer, Size, BytesRead);
  243.     BlkRead := (IoResult = 0) and (BytesRead = Size);
  244.   end;
  245.  
  246.   function FileNewer(FileA, FileB : String) : Boolean;
  247.     {-Return true if FileA is newer than FileB, both known to exist}
  248.   var
  249.     FA : file;
  250.     FB : file;
  251.     TA : LongInt;
  252.     TB : LongInt;
  253.   begin
  254.     Assign(FA, FileA);
  255.     Reset(FA);
  256.     Assign(FB, FileB);
  257.     Reset(FB);
  258.     GetFtime(FA, TA);
  259.     GetFtime(FB, TB);
  260.     Close(FA);
  261.     Close(FB);
  262.     FileNewer := (TA > TB);
  263.   end;
  264.  
  265.   procedure WriteHelp;
  266.     {-Display help information and halt}
  267.   begin
  268.     WriteLn;
  269.     WriteLn('Usage: OVRSIZ [Options] InputName [>OutputFile]');
  270.     WriteLn;
  271.     WriteLn('  OVRSIZ must read:');
  272.     WriteLn('    InputName.EXE - overlaid executable file.');
  273.     WriteLn('    InputName.MAP - symbol file for segment information.');
  274.     WriteLn;
  275.     WriteLn('Options:');
  276.     WriteLn('  /Q    Quiet mode. No status output while processing.');
  277.     Halt(1);
  278.   end;
  279.  
  280.   function ExistFile(FName : String) : Boolean;
  281.     {-Return true if file exists}
  282.   var
  283.     F : file;
  284.   begin
  285.     Assign(F, FName);
  286.     Reset(F);
  287.     if IoResult = 0 then begin
  288.       ExistFile := True;
  289.       Close(F);
  290.     end else
  291.       ExistFile := False;
  292.   end;
  293.  
  294.   procedure ValidateInput;
  295.     {-Get working filenames and assure files exist}
  296.   var
  297.     Iroot : Pathname;
  298.     Arg : String;
  299.     I : Integer;
  300.   begin
  301.     {Get parameters}
  302.     Iroot := '';
  303.     I := 1;
  304.     while I <= ParamCount do begin
  305.       Arg := StUpcase(ParamStr(I));
  306.       if (Arg = '/Q') or (Arg = '-Q') then
  307.         ShowStatus := False
  308.       else if Iroot = '' then
  309.         Iroot := Arg
  310.       else
  311.         Error('Too many filenames on command line');
  312.       Inc(I);
  313.     end;
  314.     if (Iroot = '') then
  315.       WriteHelp;
  316.  
  317.     {Build working filenames}
  318.     Iname := ForceExtension(Iroot, 'EXE');
  319.     Mname := ForceExtension(Iroot, 'MAP');
  320.  
  321.     {Make sure files are OK}
  322.     if not ExistFile(Iname) then
  323.       Error('EXE file '+Iname+' not found');
  324.     if not ExistFile(Mname) then
  325.       Error('MAP file '+Mname+' not found');
  326.     if FileNewer(Iname, Mname) then
  327.       Error('MAP file is older than EXE file');
  328.   end;
  329.  
  330.   function GetLong(var S : String; var L : LongInt) : Boolean;
  331.     {-Parse next longint out of line S}
  332.   var
  333.     Num : String[8];
  334.     Code : Word;
  335.   begin
  336.     S := TrimLead(S);
  337.     Num := '';
  338.     while (Length(S) > 0) and (Pos(S[1], Digits) <> 0) do begin
  339.       Num := Num+S[1];
  340.       Delete(S, 1, 1);
  341.     end;
  342.     if Length(Num) = 0 then begin
  343.       GetLong := False;
  344.       Exit;
  345.     end;
  346.     if (Length(S) > 0) and (Upcase(S[1]) = 'H') then begin
  347.       Num := '$'+Num;
  348.       Delete(S, 1, 1);
  349.     end;
  350.     Val(Num, L, Code);
  351.     GetLong := (Code = 0);
  352.   end;
  353.  
  354.   function GetName(var S, Name : String) : Boolean;
  355.     {-Parse next alphanumeric name from string s}
  356.   begin
  357.     S := TrimLead(S);
  358.     Name := '';
  359.     while (Length(S) > 0) and (S[1] > ' ') do begin
  360.       if Length(Name) < NameSize then
  361.         Name := Name+S[1];
  362.       Delete(S, 1, 1);
  363.     end;
  364.     GetName := (Name <> '');
  365.   end;
  366.  
  367.   function NextPara(Bytes : LongInt) : LongInt;
  368.     {-Round up to next paragraph}
  369.   begin
  370.     NextPara := (Bytes+15) and $FFFFFFF0;
  371.   end;
  372.  
  373.   procedure ParseMapFile(FName : String);
  374.     {-Read and parse the MAP file, guaranteed to exist}
  375.   var
  376.     F : Text;
  377.     S : String;
  378.     SegType : String;
  379.     Tlong : LongInt;
  380.     ParseState : (Unknown, Segments, Done);
  381.   begin
  382.  
  383.     {Open up the MAP file for reading}
  384.     Assign(F, FName);
  385.     SetTextBuf(F, Buffer, BufSize);
  386.     Reset(F);
  387.     if IoResult <> 0 then
  388.       Error('Error opening '+FName);
  389.  
  390.     if ShowStatus then
  391.       WriteLn(StdErr, 'Parsing MAP file');
  392.  
  393.     {Parse the segment description section only}
  394.     UnitCount := 0;
  395.     ParseState := Unknown;
  396.     repeat
  397.       ReadLn(F, S);
  398.       if IoResult <> 0 then
  399.         Error('Error reading '+FName);
  400.       S := StUpcase(Trim(S));
  401.       if S <> '' then
  402.         if Pos('START', S) = 1 then
  403.           ParseState := Segments
  404.         else if Pos('ADDRESS', S) = 1 then
  405.           ParseState := Done
  406.         else if ParseState = Segments then begin
  407.           {Parse the line to get the unit description}
  408.           Inc(UnitCount);
  409.           if UnitCount > MaxUnits then
  410.             Error('Cannot exceed '+Long2Str(MaxUnits)+' segments');
  411.           FillChar(Units[UnitCount], SizeOf(UnitRecord), 0);
  412.  
  413.           with Units[UnitCount] do begin
  414.  
  415.             {Get the position and size of the unit in the EXE image}
  416.             if not GetLong(S, StatStart) then
  417.               InvalidMapError;
  418.             {Ignore the end of the segment}
  419.             if not GetLong(S, Tlong) then
  420.               InvalidMapError;
  421.             {Get the length of the segment}
  422.             if not GetLong(S, StatLen) then
  423.               InvalidMapError;
  424.  
  425.             {Get the name of the segment}
  426.             if not GetName(S, Name) then
  427.               InvalidMapError;
  428.  
  429.             {Some segments are not really in the EXE file}
  430.             if not GetName(S, SegType) then
  431.               InvalidMapError;
  432.             if SegType = 'CODE' then
  433.               SegClass := 0
  434.             else if SegType = 'DATA' then
  435.               SegClass := 1
  436.             else if SegType = 'STACK' then
  437.               SegClass := 2
  438.             else if SegType = 'HEAP' then
  439.               SegClass := 3
  440.             else
  441.               SegClass := 4;
  442.           end;
  443.         end;
  444.     until (ParseState = Done) or EoF(F);
  445.     Close(F);
  446.   end;
  447.  
  448.   procedure GetEXEInfo(FName : String);
  449.     {-Open the EXE file, read its header, read the overlay information}
  450.   var
  451.     V : Integer;
  452.   begin
  453.     Assign(InF, FName);
  454.     Reset(InF, 1);
  455.     if IoResult <> 0 then
  456.       Error('Error opening EXE file '+FName);
  457.  
  458.     if ShowStatus then
  459.       WriteLn(StdErr, 'Reading EXE header information');
  460.  
  461.     {Read the existing EXE header}
  462.     if not BlkRead(InF, EXEHeader, SizeOf(ExeHeaderRec)) then
  463.       ErrorExeRead;
  464.  
  465.     with EXEHeader do begin
  466.       {Assure it's a valid EXE file}
  467.       if Signature <> $5A4D then
  468.         Error('Invalid EXE format for '+FName);
  469.  
  470.       if ShowStatus then
  471.         WriteLn(StdErr, 'Collecting overlay information');
  472.  
  473.       for V := 1 to UnitCount do
  474.         with Units[V] do
  475.           if (SegClass = 0) and (StatLen > SizeOf(OverlayRecord)) then begin
  476.             {Get to right spot in old EXE file}
  477.             Seek(InF, (LongInt(HeaderSize) shl 4)+StatStart);
  478.  
  479.             {Read the start of unit into the unit element}
  480.             if not BlkRead(InF, Ovr, SizeOf(OverlayRecord)) then
  481.               ErrorExeRead;
  482.           end else
  483.             FillChar(Ovr, SizeOf(OverlayRecord), 0);
  484.     end;
  485.     Close(InF);
  486.   end;
  487.  
  488.   procedure WriteOvrInfo;
  489.     {-Write information about the overlays}
  490.   var
  491.     V : Word;
  492.     OverBuf : LongInt;
  493.     OverSiz : LongInt;
  494.     OverSizMax : LongInt;
  495.     OverFixMax : LongInt;
  496.     StatSiz : LongInt;
  497.     DataSiz : LongInt;
  498.     StakSiz : LongInt;
  499.     StatTotal : LongInt;
  500.   begin
  501.     OverBuf := 00;
  502.     OverFixMax := 00;
  503.     OverSizMax := 00;
  504.     StatSiz := 00;
  505.     DataSiz := 00;
  506.     StakSiz := 00;
  507.  
  508.     WriteLn;
  509.     WriteLn('UNIT STATISTICS');
  510.     WriteLn('                Static  Static  Overlay  Fixup   Entry   Overlay');
  511.     WriteLn('Segment name   Segment    Size    Size    Size  Points   FilePos');
  512.     WriteLn('==============  ======   =====   =====   =====   =====   =======');
  513.     {        xxxxxxxxxxxxxxx 0FFFFh   ddddd   ddddd   ddddd   ddddd   0FFFFFh}
  514.  
  515.     for V := 1 to UnitCount do
  516.       with Units[V], Ovr do
  517.         if StatLen > 0 then begin
  518.           {Segments are paragraph-aligned}
  519.           StatLen := NextPara(StatLen);
  520.           Write(Pad(Name, NameSize+1),
  521.                 '0', HexW(StatStart shr 4), 'h   ',
  522.                 StatLen:5, '   ');
  523.           if Sig = OvrInstr then begin
  524.             {Overlaid unit}
  525.             {Segments are paragraph aligned}
  526.             CodeSize := NextPara(CodeSize);
  527.             FixupSize := NextPara(FixupSize);
  528.  
  529.             Write(CodeSize:5, '   ',
  530.                   FixupSize:5, '   ',
  531.                   EntryPts:5, '   ',
  532.                   Copy(HexL(FileOfs), 3, 6), 'h');
  533.  
  534.             {Compute overlay buffer information}
  535.             OverSiz := CodeSize+FixupSize;
  536.             if OverSiz > OverBuf then
  537.               OverBuf := OverSiz;
  538.             if FixupSize > OverFixMax then
  539.               OverFixMax := FixupSize;
  540.             inc(OverSizMax, CodeSize);
  541.             inc(StatSiz, StatLen);
  542.  
  543.           end else begin
  544.             {Non-overlaid unit or other segment}
  545.             Write('    -       -       -        -');
  546.             case SegClass of
  547.               0: inc(StatSiz, StatLen);
  548.               1: inc(DataSiz, StatLen);
  549.               2: inc(StakSiz, StatLen);
  550.             end;
  551.           end;
  552.           WriteLn;
  553.         end;
  554.  
  555.     StatTotal := StatSiz+DataSiz+StakSiz+256;
  556.     WriteLn;
  557.     WriteLn('Program segment prefix  ', 256:6);
  558.     WriteLn('Static code size        ', StatSiz:6);
  559.     WriteLn('Static data size        ', DataSiz:6);
  560.     WriteLn('Stack size              ', StakSiz:6);
  561.     WriteLn('Overlay buffer size     ', OverBuf:6, '..',
  562.                                         (OverSizMax+OverFixMax):6);
  563.     WriteLn('                        ==============');
  564.     WriteLn('Total non-heap memory   ', StatTotal+OverBuf:6, '..',
  565.                                         (StatTotal+OverSizMax+OverFixMax):6);
  566.   end;
  567.  
  568. begin
  569.   {Open standard error device}
  570.   OpenStdErr;
  571.  
  572.   {Display copyright}
  573.   WriteCopyRight;
  574.  
  575.   {Get filenames and assure they exist}
  576.   ValidateInput;
  577.  
  578.   {Parse MAP file to get segment names and locations}
  579.   ParseMapFile(Mname);
  580.  
  581.   {Read overlay information from EXE file}
  582.   GetEXEInfo(Iname);
  583.  
  584.   {Write information}
  585.   WriteOvrInfo;
  586. end.
  587.